import {Vector3} from "../math/Vector3";
import {Vector4} from "../math/Vector4";
import {RenderContext} from "../scene/RenderContext";
import {InterpolateUtil} from "../math/InterpolateUtil";
import {MathUtil} from "../math/MathUtil";

export interface IVarying {

}
export interface IAttribute {

}
export interface IUniform {

}
export interface InnerVarying {
    vPosition: Vector3;
}

export interface IFragmentResult  {
    discard: boolean;
    color: Vector4;
}

function interpolateVarying(varings: any[], interpolate: Vector3) {
    const re = {} as any;
    const [v0, v1, v2] = varings;
    for (const key of Object.keys(v0)) {
        re[key]  = InterpolateUtil.interpolateVarying(v0[key], v1[key], v2[key], interpolate);
    }
    return re;
}

export class Program<Attribute extends IAttribute, Uniform extends IUniform, Varying extends IVarying > {
    context: RenderContext;
    constructor(renderContext: RenderContext) {
        this.context = renderContext;
    }

    execute(vertexAttributes: [Attribute, Attribute, Attribute], uniform: Uniform) {
        const vertexResults: (InnerVarying & Varying)[]  = [];
        for (const attribute of vertexAttributes) {
            vertexResults.push(this.vertexShader(attribute, uniform));
        }
        // 计算到屏幕的坐标
        const canvasPoints = vertexResults.map(result => result.vPosition.appliedMatrix4(this.context.canvasMatrix))

        {
            let p0 = canvasPoints[0].clone();
            let p1 = canvasPoints[1].clone();
            let p2 = canvasPoints[2].clone();
            // 取整, 不然的话会出现异常线条
            MathUtil.roundPoint2D(p0);
            MathUtil.roundPoint2D(p1);
            MathUtil.roundPoint2D(p2);
            if (p0.y === p1.y && p0.y === p2.y) {
                console.log('共轴： ', p0, p1, p2);
                return ;
            } // y 轴共线
            // 调整位置， p0 是y最小的点， p2 是y最大的点
            if (p0.y > p1.y) {
                [p0, p1] = [p1, p0];
            }
            if (p0.y > p2.y) {
                [p0, p2] = [p2, p0];
            }
            if (p2.y < p1.y) {
                [p1, p2] = [p2, p1];
            }

            for (let y = p0.y ; y <= p2.y; y++) {
                let startX = Math.round(p0.x + (y - p0.y)*(p2.x - p0.x)/(p2.y-p0.y));
                let startZ = p0.z + (y - p0.y)*(p2.z - p0.z)/(p2.y-p0.y)
                let endX: number, endZ: number;
                if (y <= p1.y) {
                    endX = Math.round(p0.x + (y - p0.y)*(p1.x - p0.x)/(p1.y-p0.y));
                    endZ  = p0.z + (y - p0.y)*(p1.z - p0.z)/(p1.y-p0.y)
                } else {
                    endX = Math.round(p2.x +  (y - p2.y)*(p1.x - p2.x)/(p1.y-p2.y));
                    endZ = p2.z +  (y - p2.y)*(p1.z - p2.z)/(p1.y-p2.y)
                }
                if (startX > endX) {
                    [startX, endX] = [endX,  startX];
                    [startZ, endZ] = [endZ, startZ];
                }
                for (let x = startX; x <= endX; x++) {

                    // 区间判断，优化需要放到前面
                    if (x >= 0 && x<this.context.width && y>=0 && y<this.context.height) {
                        // 做插值需要使用原始数据
                        const interpolate = InterpolateUtil.barycentric(canvasPoints[0].data, canvasPoints[1].data, canvasPoints[2].data, [x,  y]);
                        // test
                        const p = InterpolateUtil.interpolateVarying(canvasPoints[0], canvasPoints[1], canvasPoints[2], interpolate)
                        // const z = interal.x * ;
                        if (this.context.depthTest(x, y, p.z))  {
                            const fragmentVarying = interpolateVarying(vertexResults, interpolate);
                            const {discard, color} = this.fragmentShader(fragmentVarying, uniform);
                            if (!discard) {
                                this.context.frameBuffer.drawPoint(x, y, color);
                                this.context.setDepth(x, y, p.z);
                            }
                        }
                    }
                }
            }
        }
    }

    proxyFragmentShader(): IFragmentResult {
        throw new Error();
    }

    vertexShader(attribute: Attribute, uniform: Uniform):  InnerVarying & Varying  {
        throw new Error();
    }

    fragmentShader(varying: InnerVarying & Varying, uniform: Uniform): IFragmentResult  {
        throw new Error();
    }
}
